package com.btcode.netty.server.session;

import com.btcode.common.MyLog;
import com.btcode.log.ILog;
import com.btcode.netty.server.session.util.ChannelUtil;
import com.sun.istack.NotNull;
import io.netty.channel.Channel;
import io.netty.util.Attribute;
import io.netty.util.AttributeKey;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

public enum SessionManager {

    //通过枚举的方式来实现单例
    INSTANCE;

    private final ILog log = MyLog.getInstance().getLogger(SessionManager.class);
    public final static String SELF_TAG = "_@#HRT";

    /**
     * 缓存用户id与对应的会话
     */
    private ConcurrentMap<String, IoSession> userId2Sessions = new ConcurrentHashMap<>();
    /* 带后缀MN处理，消息反馈时，如果对应value有值则对id添加tag后转发，会话管理不对mn与channel绑定进行更新 */
    private HashMap<String, String> userIdTag = new HashMap<>();

    public static AttributeKey<IoSession> SESSION_KEY = AttributeKey.valueOf("session");

    private static void setChannelSession(Channel channel, IoSession session) {
        Attribute<IoSession> sessionAttr = channel.attr(SESSION_KEY);
        sessionAttr.setIfAbsent(session);
    }

    /**
     * 通过用户ID向客户端发送消息
     *
     * @param userId 客户ID
     * @param msg    字节数组
     */
    public boolean sendMsg2ClientById(String userId, byte[] msg) {
        try {
            if (userIdTag.containsKey(userId + SELF_TAG)) {
                userId = userId + SELF_TAG;
            }
            if (this.userId2Sessions.get(userId) != null) {
                return this.userId2Sessions.get(userId).sendMsg(msg);
            }
        }
        catch (Exception e) {
            log.error("sendMsg2ClientById", e);
        }
        return false;
    }

    /**
     * 设置站点tag状态，主要用于发送反馈时MN已去除后缀情况，自动补上 SELF_TAG
     *
     * @param userId 客户ID
     */
    public void setUserIdTag(String userId) {
        if (!userId.contains(SELF_TAG)) {
            log.error("错误，接口使用只针对MN带后缀" + SELF_TAG);
            return;
        }
        userIdTag.put(userId, "");
    }

    /**
     * 通过channel获取会话信息
     *
     * @param channel 客户Channel
     * @return
     */
    public IoSession getSessionByChannel(Channel channel) {
        return channel.attr(SESSION_KEY).get();
    }

    /**
     * 通过用户ID获取会话信息
     *
     * @param userId 客户ID
     * @return
     */
    public IoSession getSessionById(String userId) {
        return this.userId2Sessions.get(userId);
    }

    /**
     * 注册会话
     *
     * @param userId  客户ID
     * @param channel 客户Channel
     */
    public void registerSession(@NotNull String userId, Channel channel) {
        IoSession session = getSessionByChannel(channel);
        if (session == null) {
            session = new IoSession(channel);
            setChannelSession(channel, session);
        }
        //表示下端没按协议（一个连接ID不一致）
        if (session.getUserId() != null && !session.getUserId().equals(userId)) {
            log.error(String.format("协议错误，下端ID不一致，old:%s，new:%s，IP:%s", session.getUserId(), userId, ChannelUtil.getIp(channel)));
            channel.close();
            return;
        }
        session.setUserId(userId);
        session.setLastTime(System.currentTimeMillis());
        userId2Sessions.put(userId, session);
        log.info(String.format("会话接入：%s", session));
    }

    /**
     * 通过channel清理会话
     *
     * @param channel 客户Channel
     */
    public void unRegisterUserContext(Channel channel) {
        if (channel == null) {
            return;
        }
        IoSession session = getSessionByChannel(channel);
        if (session == null) {
            log.info(String.format("会话清除：%s", ChannelUtil.getIp(channel)));
            channel.close();
            return;
        }
        //使用安全策略删除并发集合
        String userId = session.getUserId();
        for (Iterator<Map.Entry<String, IoSession>> it = userId2Sessions.entrySet().iterator(); it.hasNext(); ) {
            Map.Entry<String, IoSession> u2s = it.next();
            if (u2s.getKey().equals(userId)) {
                it.remove();
                break;
            }
        }
        session.close();
        userIdTag.remove(userId);
        log.info(String.format("会话清除：%s", session));
    }

    /**
     * 通过ID清理会话
     *
     * @param userId 客户ID
     */
    public void unRegisterUserContext(String userId) {
        for (Iterator<Map.Entry<String, IoSession>> it = userId2Sessions.entrySet().iterator(); it.hasNext(); ) {
            Map.Entry<String, IoSession> u2s = it.next();
            if (u2s.getKey().equals(userId)) {
                u2s.getValue().close();
                log.info(String.format("会话清除：%s", u2s.getValue()));
                it.remove();
                break;
            }
        }
        userIdTag.remove(userId);
    }
}  